#!/bin/bash
#
# Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
#
# SPDX-License-Identifier: LGPL-2.1-only

TEST_DESC="UST tracer - Tracing with per UID buffers and periodical flush"

CURDIR=$(dirname "$0")/
TESTDIR="$CURDIR/../../.."
NR_ITER=100
NR_USEC_WAIT=100000
TESTAPP_PATH="$TESTDIR/utils/testapp"
TESTAPP_NAME="gen-ust-events"
TESTAPP_BIN="$TESTAPP_PATH/$TESTAPP_NAME/$TESTAPP_NAME"
SESSION_NAME="periodical-flush"
EVENT_NAME="tp:tptest"
NUM_TESTS=38
APP_TMP_FILE=$(mktemp -u -t tmp.test_periodical_metadata_flush_ust_app_tmp_file.XXXXXX)
APP_PIDS=()

# shellcheck source-path=SCRIPTDIR/../../../
source "$TESTDIR/utils/utils.sh"

if [ ! -x "$TESTAPP_BIN" ]; then
	BAIL_OUT "No UST events binary detected."
fi

# MUST set TESTDIR before calling those functions

function enable_channel_per_uid()
{
	local sess_name=$1
	local channel_name=$2

	"$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-channel --buffers-uid -u "$channel_name" -s "$sess_name" --switch-timer 100000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_channel_per_pid()
{
	local sess_name=$1
	local channel_name=$2

	"$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-channel --buffers-pid -u "$channel_name" -s "$sess_name" --switch-timer 100000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_metadata_per_uid()
{
	local sess_name=$1
	local channel_name="metadata"

	"$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-channel --buffers-uid -u "$channel_name" -s "$sess_name" --switch-timer 200000  2>&1
	ok $? "Enable channel $channel_name per UID for session $sess_name"
}

function enable_metadata_per_pid()
{
	local sess_name=$1
	local channel_name="metadata"

	"$TESTDIR/../src/bin/lttng/$LTTNG_BIN" enable-channel --buffers-pid -u "$channel_name" -s "$sess_name" --switch-timer 200000 >/dev/null 2>&1
	ok $? "Enable channel $channel_name per PID for session $sess_name"
}

function validate_trace()
{
	local out

	out=$("_run_babeltrace_cmd" "$TRACE_PATH" | grep -c $EVENT_NAME)
	if [ "$out" -eq 0 ]; then
		fail "Trace validation"
		diag "No event(s) found. We are supposed to have at least one."
		out=1
	else
		pass "Trace validation"
		diag "Found $out event(s). Coherent."
		out=0
	fi

	return $out
}

function check_app_tmp_file()
{
	# Wait for the application file to appear indicating that at least one
	# tracepoint has been fired.
	while [ ! -f "$APP_TMP_FILE" ]; do
		sleep 0.5
	done
	diag "Removing test app temporary file $APP_TMP_FILE"
	rm -f "$APP_TMP_FILE"
}

function start_trace_app()
{
	# Start application with a temporary file.
	$TESTAPP_BIN -i $NR_ITER -w $NR_USEC_WAIT --sync-after-first-event "$APP_TMP_FILE" &
	ret=$?
	APP_PIDS+=(${!})
	ok $ret "Start application to trace"
}

function start_check_trace_app()
{
	start_trace_app
	check_app_tmp_file
}


function wait_trace_apps()
{
	wait "${APP_PIDS[@]}" 2> /dev/null
	APP_PIDS=()
}

test_after_app_pid() {
	local out

	APP_PIDS=()

	diag "Start application AFTER tracing is started"

	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $SESSION_NAME

	# Start application after tracing
	start_check_trace_app
	# After this point we are sure that at least one event has been hit.

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	kill -s SIGSTOP "${APP_PIDS[@]}"

	# Give time to the consumer to write inflight data.
	sleep 2

	# shellcheck disable=SC2119
	validate_trace
	out=$?

	kill -s SIGKILL "${APP_PIDS[@]}"
	wait "${APP_PIDS[@]}" 2>/dev/null
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

test_before_app_pid() {
	local out

	APP_PIDS=()

	diag "Start application BEFORE tracing is started"

	start_trace_app

	# Start application before tracing
	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	enable_metadata_per_pid $SESSION_NAME
	enable_channel_per_pid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $SESSION_NAME

	check_app_tmp_file
	# Let the application at least perform a flush!
	sleep 2

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	kill -s SIGSTOP "${APP_PIDS[@]}"

	# Give time to the consumer to write inflight data.
	sleep 2

	# shellcheck disable=SC2119
	validate_trace
	out=$?

	kill -s SIGKILL "${APP_PIDS[@]}"
	wait "${APP_PIDS[@]}" 2>/dev/null

	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

test_after_app_uid() {
	local out

	APP_PIDS=()

	diag "Start application AFTER tracing is started"

	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $SESSION_NAME

	# Start application after tracing
	start_check_trace_app
	# After this point we are sure that at least one event has been hit.

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	kill -s SIGSTOP "${APP_PIDS[@]}"

	# Give time to the consumer to write inflight data.
	sleep 2

	# shellcheck disable=SC2119
	validate_trace
	out=$?

	kill -s SIGKILL "${APP_PIDS[@]}"
	wait "${APP_PIDS[@]}"
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

test_before_app_uid() {
	local out

	APP_PIDS=()

	diag "Start application BEFORE tracing is started"

	# Start application before tracing
	start_trace_app

	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	enable_metadata_per_uid $SESSION_NAME
	enable_channel_per_uid $SESSION_NAME "channel0"
	enable_ust_lttng_event_ok $SESSION_NAME $EVENT_NAME "channel0"
	start_lttng_tracing_ok $SESSION_NAME

	check_app_tmp_file
	# Let the application at least perform a flush!
	sleep 2

	# Make sure the application does not generate any more data,
	# thus ensuring that we are not flushing a packet concurrently
	# with validate_trace.
	kill -s SIGSTOP "${APP_PIDS[@]}"

	# Give time to the consumer to write inflight data.
	sleep 2

	# shellcheck disable=SC2119
	validate_trace
	out=$?

	kill -s SIGKILL "${APP_PIDS[@]}"
	wait "${APP_PIDS[@]}"
	stop_lttng_tracing_ok $SESSION_NAME
	destroy_lttng_session_ok $SESSION_NAME

	wait_trace_apps

	return $out
}

# MUST set TESTDIR before calling those functions
plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"
bail_out_if_no_babeltrace

TESTS=(
	"test_before_app_uid"
	"test_after_app_uid"
	"test_before_app_pid"
	"test_after_app_pid"
)

TEST_COUNT=${#TESTS[@]}
i=0

# shellcheck disable=SC2119
start_lttng_sessiond

while [ $i -lt "$TEST_COUNT" ]; do
	TRACE_PATH=$(mktemp -d -t tmp.test_periodical_metadata_flush_ust_trace_path.XXXXXX)
	${TESTS[$i]}
	rm -rf "$TRACE_PATH"
	(( "i++" ))
done

# shellcheck disable=SC2119
stop_lttng_sessiond
